package com.zyong.encryption.server.netty.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.ChannelHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.handler.timeout.IdleState;
import org.apache.commons.lang3.StringUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
import com.zyong.encryption.common.holder.LogUtil;
import io.netty.channel.ChannelInboundHandlerAdapter;
import com.zyong.encryption.common.netty.bean.Content;
import com.zyong.encryption.server.socket.SocketManager;
import com.zyong.encryption.common.netty.bean.HeartBeat;
import com.zyong.encryption.common.enums.NettyResultEnum;
import com.zyong.encryption.server.service.EncryptionService;
import org.springframework.beans.factory.annotation.Autowired;
import com.zyong.encryption.common.enums.NettyMessageActionEnum;

/**
 * NettyServerMessageHandler.
 * @author xiaoyu
 */
@ChannelHandler.Sharable
@Component
public class NettyServerMessageHandler extends ChannelInboundHandlerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerMessageHandler.class);

    private final EncryptionService encryptionService;

    @Autowired
    public NettyServerMessageHandler(final EncryptionService encryptionService) {
        this.encryptionService = encryptionService;
    }

    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
        HeartBeat hb = (HeartBeat) msg;
        try {
            final NettyMessageActionEnum actionEnum = NettyMessageActionEnum.acquireByCode(hb.getAction());
            LogUtil.debug(LOGGER, "receive client data this action:{}", () -> hb);
            Boolean success;
            switch (actionEnum) {
                case HEART:
                    hb.setAction(NettyMessageActionEnum.HEART.getCode());
                    ctx.writeAndFlush(hb);
                    break;
                case ENCRYPT:
                    Content content = hb.getContent();
                    String encryptionContent = encryptionService.encode(content.getOriginContent());
                    content.setEncryptionContent(encryptionContent);
                    if (StringUtils.isNotEmpty(encryptionContent)) {
                        success = true;
                    } else {
                        success = false;
                    }
                    ctx.writeAndFlush(buildSendMessage(hb.getKey(), content, NettyMessageActionEnum.ENCRYPT.getCode(), success));
                    break;
                case DECIPHERING:
                    Content content2 = hb.getContent();
                    String originContent = encryptionService.decode(content2.getEncryptionContent());
                    content2.setOriginContent(originContent);
                    if (StringUtils.isNotEmpty(originContent)) {
                        success = true;
                    } else {
                        success = false;
                    }
                    ctx.writeAndFlush(buildSendMessage(hb.getKey(), content2, NettyMessageActionEnum.DECIPHERING.getCode(), success));
                    break;
                default:
                    hb.setAction(NettyMessageActionEnum.HEART.getCode());
                    ctx.writeAndFlush(hb);
                    break;
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        //是否到达最大上线连接数
        if (SocketManager.getInstance().isAllowConnection()) {
            SocketManager.getInstance().addClient(ctx.channel());
        } else {
            ctx.close();
        }
    }

    @Override
    public void channelRegistered(final ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
        SocketManager.getInstance().removeClient(ctx.channel());
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelReadComplete(final ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) {
        //心跳配置
        if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                ctx.close();
            }
        }
    }

    private HeartBeat buildSendMessage(final String key, final Content content, int action, final Boolean success) {
        HeartBeat heartBeat = new HeartBeat();
        heartBeat.setKey(key);
        heartBeat.setContent(content);
        heartBeat.setAction(action);
        if (success) {
            heartBeat.setResult(NettyResultEnum.SUCCESS.getCode());
        } else {
            heartBeat.setResult(NettyResultEnum.FAIL.getCode());
        }
        return heartBeat;
    }

}